#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#include "log.h"

using namespace std;

extern const char * const strerr(int err);

#define LOG_PATH	"/tmp/hltlc.log"

static const char * const log_level[] = {
	"EMERG",
	"ALERT",
	"CRITC",
	"ERROR",
	" WARN",
	" NOTI",
	" INFO",
	"DEBUG"
};

log::log() {
	strncpy(log_path, LOG_PATH, 256);
	log_file = STDERR_FILENO;
	log_period = 1024;
	log_maxsize = 8 * 1024 * 1024;
	rotate_num = 3;
	pthread_mutex_init(&lock, NULL);
}

void log::openlog(const char * file = NULL) {
	//system("/bin/date >> " LOG_PATH);
	if (file != NULL)
		strncpy(log_path, file, 256);

	int fd = open(log_path, O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK);
	if (fd != -1) {
		log_file = fd;
	}
}

void log::logging(int level, const char * fmt, ...) {
	extern uint64_t NOW(void);
	//volatile int err_no = errno;
	static int _count = 0;

	pthread_mutex_lock(&lock);
	if ((_count > log_period) && (STDERR_FILENO != log_file)) {
		struct stat s;
		stat(log_path, &s);
		if (s.st_size > log_maxsize) {
			close(log_file);
			logrotate(log_path, rotate_num);
			openlog(log_path);
		}
		_count = 0;
	}
	_count++;

	level = level & 7;

	static char *buf = NULL;
	if (buf == NULL) {
		buf = (char *)malloc(1024*1024);
		if (buf == NULL) {
			pthread_mutex_unlock(&lock);
			printf("alloc log buffer failed!");
			return ;
		}
	}

	int offset = 0;
	time_t t;
	struct tm *tm;

	t = time(NULL);
	tm = localtime(&t);

	if (tm != NULL)
		offset += strftime(&buf[offset], 32, "%Y%m%d %H:%M:%S ", tm);
	offset += snprintf(&buf[offset], 16, "[%s] ", log_level[level]);

	va_list args;

	va_start(args, fmt);
	offset += vsnprintf(&buf[offset], 1000 * 1024, fmt, args);
	va_end(args);

	if (buf[offset-1] != '\n') {
		buf[offset] = '\n';
		offset++;
	}

	write(log_file, buf, offset);
	pthread_mutex_unlock(&lock);
}

void log::logrotate(const char * logfile, int num)
{
	const size_t filename_length = strlen(logfile);
	const size_t max_length = filename_length + 3;
	char *file_list, *p;

	if (num > 9)
		num = 9;

	file_list = (char *)malloc((num+1) * max_length);
	if (file_list == NULL) {
		printf("logrotate: %s", strerr(errno));
		return;
	}

	strcpy(file_list, logfile); // in case (i == 0)
	p = file_list + max_length;
	for (int i = 1; i <= num; i++) {
		sprintf(p, "%s.%d", logfile, i);
		p += max_length;
	}

	unlink(p); // p == file_list[num * max_length]
	p -= max_length;
	while (p >= file_list) {
		rename(p, p + max_length);
		p -= max_length;
	}

	free(file_list);
}

log log;

